﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cryptlib;

namespace EncryptNotesAddIn
{
    public sealed class Encryption
    {
        private static bool CryptLibInitialized = false;

        private static void Initialize()
        {
            if (CryptLibInitialized == false)
            {
                crypt.Init();
                crypt.AddRandom(crypt.RANDOM_SLOWPOLL);
                CryptLibInitialized = true;
            }
        }

        public static void FinishEncryption()
        {
            crypt.End();
        }

        public static string EncryptData(string message, string password)
        {
            Initialize();

            int envelope = crypt.CreateEnvelope(crypt.UNUSED, crypt.FORMAT_CRYPTLIB);
            crypt.SetAttributeString(envelope, crypt.ENVINFO_PASSWORD, password);
            crypt.SetAttribute(envelope, crypt.ENVINFO_DATASIZE, message.Length);
            int bytesCopied = crypt.PushData(envelope, message);
            crypt.FlushData(envelope);
            byte[] envelopedData = new byte[bytesCopied + 4000];
            bytesCopied = crypt.PopData(envelope, envelopedData, envelopedData.Length);
            byte[] result = envelopedData.Take(bytesCopied).ToArray();
            string encryptedData = BytesToString(result);
            crypt.DestroyEnvelope(envelope);
            return encryptedData;
        }

        
        public static string DecryptData(string encodedData, string password)
        {
            Initialize();

            byte[] envelopedData = StringToBytes(encodedData);
            int envelope = crypt.CreateEnvelope(crypt.UNUSED, crypt.FORMAT_AUTO);

            int bytesCopied = 0;
            try
            {
                bytesCopied = crypt.PushData(envelope, envelopedData);
            }
            catch (CryptException cexc)
            {
                //should only be password
            }

            crypt.SetAttributeString(envelope, crypt.ENVINFO_PASSWORD, password);
            crypt.FlushData(envelope);
            byte[] messageBuffer = new byte[envelopedData.Length];
            bytesCopied = crypt.PopData(envelope, messageBuffer, messageBuffer.Length);
            messageBuffer = messageBuffer.Take(bytesCopied).ToArray();
            string unencryptedData = ASCIIEncoding.ASCII.GetString(messageBuffer);
            crypt.DestroyEnvelope(envelope);
            return unencryptedData;
        }

        private static string BytesToString(byte[] data)
        {
            StringBuilder sb = new StringBuilder();
            foreach (byte datum in data)
            {
                sb.Append(datum.ToString().PadLeft(3, '0'));
            }
            return sb.ToString();
        }

        private static byte[] StringToBytes(string data)
        {
            List<byte> results = new List<byte>();
            for (int i = 0; i < data.Length; i += 3)
            {
                byte outbyte;
                byte.TryParse(data.Substring(i, 3), out outbyte);
                results.Add(outbyte);
            }
            return results.ToArray();
        }
    }
}
